%{
#define YYDEBUG  1
#include <iostream>
#include "grammar.hpp"
using namespace llvm;
using std::cout;
using std::cerr;
using std::endl;
using std::move;

extern Expr* program;

extern "C" {
    extern char* yytext;
    // extern int yylineno;
    void yyerror(const char* s){
        // printf("line %d:  %s  at  '%s'  \n",yylineno,s,yytext);
        printf("[ERROR]  %s  at  '%s'  \n", s, yytext);
    }
    extern int yylex();
}
%}

%code requires{
    #include <YY/type.hpp>
}

%token FJIDENTIFIER FJINTEGER FJDOUBLE FJSTRING FJCHAR FJTRUE FJFALSE
%token VOID BOOL CHAR INT FLOAT DOUBLE STR
%token BOOLRE CHARRE INTRE FLOATRE DOUBLERE STRRE
%token EQEQ NEQ LT LOE GT GOE
%token LPAREN RPAREN LBRACE RBRACE LBRACKET RBRACKET DOT COMMA SEMI COLON
%token ADD SUB MUL DIV MOD EQ
%token AND OR NOT XOR
%token SELFINC SELFDEC
%token SL SR
%token IF ELSE RETURN FOR WHILE BREAK STRUCT CLASS FUN CONTINUE
%token PRIVATE PUBLIC PROTECTED OVERRIDE SUPER AS
%token VAL VAR

%right "body" "if_stmt"

%left OR 
%left AND
%left XOR
%left EQEQ NEQ
%left GT GOE LT LOE
%left ADD SUB
%left MUL DIV MOD
%right SELFINC SELFDEC
%left LBRACE RBRACE DOT

%start program

%%
program : top_list { program = Expr::sequenceOf({$1.node}); }
;

top_list    :   top_decl { $$ = move($1); }
            |   top_list top_decl { $$.node = Expr::sequenceOf({ $1.node, $2.node }); }
;

top_decl    :   func_decl   { $$ = move($1); }
            |   class_decl  { $$ = move($1); }
            |   struct_decl { $$ = move($1); }
;
            // TODO: var_decl

block   :   LBRACE inblock_decl_list RBRACE { $$ = move($2); }
        |   LBRACE RBRACE                   { printf("Empty block here \n"); }
;

inblock_decl_list   :   inblock_last_decl                   { $$ = move($1); }
                    |   inblock_mid_list inblock_last_decl  { $$.node = Expr::sequenceOf({ $1.node, $2.node }); }
;

inblock_last_decl   :   non_semi_decl   { $$ = move($1); }
                    |   semi_decl       { $$ = move($1); } // 原来需要分号，但免除分号
                    |   semi_decl SEMI  { $$ = move($1); }
;

inblock_mid_list    :   inblock_mid_decl                    { $$ = move($1); }
                    |   inblock_mid_list inblock_mid_decl   { $$.node = Expr::sequenceOf({ $1.node, $2.node }); }
;

inblock_mid_decl    :   non_semi_decl   { $$ = move($1); }
                    |   semi_decl SEMI  { $$ = move($1); }  // 不允许省略分号
;

non_semi_decl   :   if_stmt     { $$ = move($1); }
                |   for_stmt    { $$ = move($1); }
                |   while_stmt  { $$ = move($1); }
;

semi_decl   :   var_decl    { $$ = move($1); }
            |   expr        { $$ = move($1); }  // if_stmt 也是expr，此处要加分号
            |   RETURN expr { $$.node = new Return($2.node); }
            |   BREAK       { $$.node = new Break(); }    //TODO:
            |   CONTINUE       { $$.node = new Continue(); }    //TODO:
;

expr    :   assign { $$ = move($1); }
        |   name LPAREN init_exprs RPAREN conver_type{ $$.node = new Call(move($1.content), $3.initArgs); }  //TODO:
        |   name { printf("expr is name\n"); $$.node = new Var(move($1.content)); }
        |   SELFINC name { $$.node = BinaryOp::assign(new Var($2.content), new BinaryOp(OpType::Add, new Var($2.content), new Int(1))); }
        |   SELFDEC name { $$.node = BinaryOp::assign(new Var($2.content), new BinaryOp(OpType::Sub, new Var($2.content), new Int(1))); }
        |   struct_mem_name { $$ = move($1); }
        |   literal { $$ = move($1); }
        |   op_expression { $$ = move($1); }
        //  | SUB expr {}  //TODELETE:
        //  | NOT expr {}  //TODELETE:
        |   arr { printf("expr is arr\n"); $$ = move($1); }   
        |   if_stmt     { $$ = move($1); }
;

init_exprs :  {}
           | init_exprs_detail { $$ = move($1); }
;

init_exprs_detail : expr { $$.initArgs.push_back($1.node); }
           | init_exprs_detail COMMA expr {$1.initArgs.push_back($3.node);
                                           $$ = move($1); } 
;

struct_mem_name :   struct_mem_prefix DOT strcut_mem_part {
                        $$.node = new DotAST($1.node, $3.node);
                    }
;

struct_mem_prefix   :   strcut_mem_part { $$ = move($1); }
                    |   struct_mem_prefix DOT strcut_mem_part {
                            $$.node = new DotAST($1.node, $3.node);
                        }
;

strcut_mem_part : name { $$.node = new Var(move($1.content)); }
                | arr { $$ = move($1); }
;

// XXX： 危险慎用
// decl    :   non_semi_decl { $$ = move($1); }
//         |   semi_decl { $$ = move($1); }
// ;

type    :   basic_type { $$ = move($1); }
        |   array_type { $$ = move($1); }    
        |   struct_type { $$ = move($1); }   
        |   class_type {}    //TODO:
;

basic_type  :   VOID    { $$ = move($1); }
            |   BOOL    { $$ = move($1); }
            |   CHAR    { $$ = move($1); }
            |   INT     { $$ = move($1); }
            |   FLOAT   { $$ = move($1); }
            |   DOUBLE  { $$ = move($1); } 
            |   STR     { $$ = move($1); }
;

refer_basic_type    :   BOOLRE      { $$ = move($1); }
                    |   CHARRE      { $$ = move($1); }
                    |   INTRE       { $$ = move($1); }
                    |   FLOATRE     { $$ = move($1); }
                    |   DOUBLERE    { $$ = move($1); }
                    |   STRRE       { $$ = move($1); }
;

array_type  :   basic_type LBRACKET RBRACKET {
                    $$.type = llvm::ArrayType::get($1.type, 0);
                }

            |   basic_type LBRACKET RBRACKET array_size {
                    $$.type = $1.type;
                    $$.arraySizes = move($4.arraySizes);
                    for (uint64_t siz : $$.arraySizes)
                        $$.type = llvm::ArrayType::get($$.type, siz);
                    $$.type = llvm::ArrayType::get($$.type, 0);
                }

            |   basic_type array_size {
                    $$.type = $1.type;
                    $$.arraySizes = move($2.arraySizes);
                    for (uint64_t siz : $$.arraySizes)
                        $$.type = llvm::ArrayType::get($$.type, siz);
                    cerr << "Final array_type: ";
                    print_t($$.type);
                }
;

array_size  :   LBRACKET FJINTEGER RBRACKET array_size { 
                    $$.arraySizes= move($4.arraySizes); $$.arraySizes.push_back($2.iValue);
                    cerr << "Now array size (reversed):";
                    for (uint64_t siz : $$.arraySizes)
                        cerr << " " << siz;
                    cerr << endl;
                }

            |   LBRACKET FJINTEGER RBRACKET {
                    $$.arraySizes.push_back($2.iValue); 
                    cerr << "Now array size (reversed):";
                    for (uint64_t siz : $$.arraySizes)
                        cerr << " " << siz;
                    cerr << endl;
                }
;

struct_type : STRUCT name { $$ = move($2); $$.type = theScope.findType(move($$.content));}
;

class_type : CLASS name {}  //TODO:
;

var_decl : access VAL val_detail { $$ = move($3); } //TODO:
         | access VAR var_detail { $$ = move($3); } //TODO:
;

val_detail : var_define EQ expr { $$.node = new Var(move($1.content), true, true, false, $3.node, $1.type); }
           | var_define EQ LBRACKET init_exprs RBRACKET { $$.node = new ArrayPtr(move($1.content), true, true, $4.initArgs, false, $1.type); }
;

var_detail : var_define_pre { $$ = move($1); }
           | var_define EQ expr { $$.node = new Var(move($1.content), true, false, false, $3.node, $1.type); }
           | var_define EQ LBRACKET init_exprs RBRACKET {
               printf("var_define = [...]\n");
               $$.node = new ArrayPtr(move($1.content), true, false, $4.initArgs, false, $1.type);
               }  
;

//TODO:
var_define_pre : name { printf("var_define_pre is name\n"); $$.node = new Var(move($1.content), true, false, false, nullptr, nullptr); }
               | name COLON basic_type { printf("var_define_pre has basic_type\n"); $$.node = new Var(move($1.content), true, false, false, nullptr, $3.type); }
               | name COLON array_type { printf("var_define_pre has array_type\n"); $$.node = new ArrayPtr(move($1.content), true, false, nullptr, false, $3.type); }
               | name COLON struct_type { printf("var_define_pre has struct_type\n"); $$.node = new Var(move($1.content), true, false, false, nullptr, $3.type); }
;

var_define : name { printf("var_define is name\n"); assert($1.type == nullptr); $$ = move($1); /*$$.type = nullptr; XXX*/ }
           | name COLON basic_type { printf("var_define has basic_type\n"); $$.content = move($1.content); $$.type = $3.type; }
           | name COLON array_type { printf("var_define has array_type\n"); $$.content = move($1.content); $$.type = $3.type; }
           | name COLON struct_type { printf("var_define has struct_type\n"); $$.content = move($1.content); $$.type = $3.type; }
;

func_decl   :   former_part latter_part {
                    // cout << "body = " << $2.node << endl;
                    $$.node = new FunctionAST(dynamic_cast<PrototypeAST *>($1.node), $2.node);
                }
;

//FIXME: TODO:
former_part : limit_fun access FUN name LPAREN func_args RPAREN { $$.node = new PrototypeAST(move($4.content), $6.funParaTypes, $6.funParaNames, $6.funParaIsRefs, BasicType::VoidTy); }
            | limit_fun access FUN name LPAREN func_args RPAREN COLON type { $$.node = new PrototypeAST(move($4.content), $6.funParaTypes, $6.funParaNames, $6.funParaIsRefs, $9.type); }
            // | limit_fun access FUN name LPAREN func_args RPAREN COLON basic_type { $$.node = new PrototypeAST(move($4.content), $6.funParaTypes, $6.funParaNames, $6.funParaIsRefs, $9.type); }
            // | limit_fun access FUN name LPAREN func_args RPAREN COLON array_type { $$.node = new PrototypeAST(move($4.content), $6.funParaTypes, $6.funParaNames, $6.funParaIsRefs, $9.type); }
            // | limit_fun access FUN name LPAREN func_args RPAREN COLON struct_type { $$.node = new PrototypeAST(move($4.content), $6.funParaTypes, $6.funParaNames, $6.funParaIsRefs, theScope.findType($9.content)); }
;

latter_part :   block               { $$ = move($1); }
            |   EQ inblock_mid_decl { $$ = move($2); } // 不可免除分号
;


func_args :   {}
          | func_args_detail { $$ = move($1); }
;

func_args_detail : func_var_decl { $$ = move($1); }
                 | func_args_detail COMMA func_var_decl {$1.funParaTypes.push_back($3.funParaTypes[0]);
                                                         $1.funParaNames.push_back($3.funParaNames[0]);
                                                         $1.funParaIsRefs.push_back($3.funParaIsRefs[0]);
                                                         $$ = move($1); }
;

func_var_decl :  name COLON func_args_type { $$.funParaTypes.push_back($3.type);
                                            $$.funParaNames.push_back(move($1.content));
                                            $$.funParaIsRefs.push_back($3.isFunParaRef); }
;

func_args_type : basic_type {
                    $$ = move($1);
                }
               | array_type {
                    $$ = move($1);
                    $$.type = $$.type->getPointerTo();
               }
               | refer_basic_type { $$ = move($1); }
;

name : FJIDENTIFIER { assert($1.type == nullptr && "name should have no type"); $$ = move($1); }
;

literal_number : FJINTEGER { $$.node = new Int($1.iValue); }
                | FJDOUBLE { $$.node = new Float($1.dValue); }
                | SUB FJINTEGER { $$.node = new Int(-$2.iValue); }
                | SUB FJDOUBLE { $$.node = new Float(-$2.dValue); }
;

literal_char : FJCHAR {$1.node = new Char($1.c); $$ = move($1); }
;

literal_bool : FJTRUE {$1.node = new Bool($1.iValue); $$ = move($1); }
             | FJFALSE {$1.node = new Bool($1.iValue); $$ = move($1); }
;

literal_string : FJSTRING {$1.node = new StringPtr($1.content); $$ = move($1); }
;

literal : literal_number    { $$ = move($1); }
        | literal_char      { $$ = move($1); }
        | literal_bool      { $$ = move($1); }
        | literal_string    { $$ = move($1); }
;

//TODO:
conver_type :  {}
            | AS name {}
;

op_expression : expr OR expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); }
              | expr AND expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); }
              | expr XOR expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); }
              | expr EQEQ expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); }
              | expr NEQ expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); }
              | expr GT expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); }
              | expr GOE expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); }
              | expr LT expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); }
              | expr LOE expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); }
              | expr ADD expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); }
              | expr SUB expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); }
              | expr MUL expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); }
              | expr DIV expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); }
              | expr MOD expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); }
              | LPAREN expr RPAREN { $$ = move($2); }
;

arr :  name LBRACKET expr RBRACKET { $$.node = new ArrayPtr(move($1.content), $3.node); }
    |  arr LBRACKET expr RBRACKET { $$.node = new ArrayPtr(static_cast<ArrayPtr*>($1.node), $3.node); }  
;

assign : name EQ expr { $$.node = new BinaryOp($2.op, new Var(move($1.content)), $3.node); }
       | arr EQ expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); } 
       | struct_mem_name EQ expr { $$.node = new BinaryOp($2.op, $1.node, $3.node); } 
;

// assign_expr : expr { $$ = move($1); }
//             | if_stmt { $$ = move($1); }
// ;

if_stmt //: IF LPAREN expr RPAREN if_body ELSE if_stmt %prec "if_stmt" %expect-rr 1 { $$.node = new If($3.node, $5.node, $7.node); }
        : IF LPAREN expr RPAREN if_body ELSE if_body %prec "body" { $$.node = new If($3.node, $5.node, $7.node); 
                                                // cout << "node = " << $$.node << endl;
                                              }
        | IF LPAREN expr RPAREN if_body %prec "body" %expect 1 { $$.node = new If($3.node, $5.node, nullptr); }
;

// if body 因有返回值，语法比较特殊，单独语句可以省略分号
if_body :   block               { $$ = move($1); }
        |   inblock_last_decl   { $$ = move($1); } // 可以省略分号

// 一般的body，单独语句不允许省略分号
body    :   block               { $$ = move($1); }
        |   inblock_mid_decl    { $$ = move($1); }  // 单独语句不能省略分号
;

for_stmt : FOR LPAREN first_for_con SEMI expr SEMI last_for_con RPAREN body { $$.node = new For($3.node, $5.node, $7.node, $9.node); }
;

first_for_con :  {}
              | expr { $$ = move($1); }
              | VAR var_detail { $$ = move($2); }
;

last_for_con :  {}
             | expr { $$ = move($1); }
;

while_stmt : WHILE LPAREN expr RPAREN body { $$.node = new While($3.node, $5.node); }
;

struct_decl : STRUCT name LBRACE struct_mem RBRACE { $$.node = new Struct(move($2.content), $4.initArgs); }
;

struct_mem :  {}
           | var_decl SEMI{ $$.initArgs.push_back($1.node); }
           | func_decl { $$.initArgs.push_back($1.node); }
           | struct_mem var_decl SEMI{ $1.initArgs.push_back($2.node); $$ = move($1); }
           | struct_mem func_decl { $1.initArgs.push_back($2.node); $$ = move($1); }
;

//TODO:
class_decl : CLASS name inherit_part LBRACE class_mem RBRACE {}
;

//TODO:
inherit_part :  {}
             | COLON name {}
;

//TODO:
class_mem : construct {}
          | struct_mem {}
          | class_mem struct_mem {}
          | class_mem construct {} 
;

//TODO:
construct : con_former con_latter block {}
;

//TODO:
con_former : name LPAREN func_args RPAREN {}
;

//TODO:
con_latter :  {}
           | SUPER LPAREN func_args RPAREN {}
;

//TODO:
access :  {}
       | PRIVATE {}
       | PUBLIC {}
       | PROTECTED {}
;

//TODO:
limit_fun :  {}
          | OVERRIDE {}
;

%%

